package com.mj.myvirtualapp.hooktest;

import android.app.Activity;
import android.app.Application;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.util.Log;
import android.util.Printer;
import android.widget.Toast;

import com.github.moduth.blockcanary.BlockCanary;
import com.github.moduth.blockcanary.BlockCanaryContext;
import com.github.moduth.blockcanary.internal.BlockInfo;

import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public class MyApp extends Application {
    public String TAG = "MyApp";
    List<WeakReference<Activity>> weakReferenceList = new ArrayList<>();
    private boolean mStartedPrinting;

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        hookAMS();
        registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
            @Override
            public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
                Log.e(TAG, "onActivityCreated: " + activity.getClass().getName());
            }

            @Override
            public void onActivityStarted(Activity activity) {
                Log.e(TAG, "onActivityStarted: " + activity.getClass().getName());
            }

            @Override
            public void onActivityResumed(Activity activity) {
                Log.e(TAG, "onActivityResumed: " + activity.getClass().getName());
            }

            @Override
            public void onActivityPaused(Activity activity) {
                Log.e(TAG, "onActivityPaused: " + activity.getClass().getName());
            }

            @Override
            public void onActivityStopped(Activity activity) {
                Log.e(TAG, "onActivityStopped: " + activity.getClass().getName());
            }

            @Override
            public void onActivitySaveInstanceState(Activity activity, Bundle outState) {

            }

            @Override
            public void onActivityDestroyed(Activity activity) {
                Log.e(TAG, "onActivityDestroyed: " + activity.getClass().getName());
                weakReferenceList.add(new WeakReference<Activity>(activity));
            }
        });
//        Debug.dumpHprofData();

        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    Runtime.getRuntime().gc();
//                    List removeList = new ArrayList();
                    for (WeakReference<Activity> weakReference : weakReferenceList) {
                        Activity activity = weakReference.get();
                        Log.e(TAG, "run: " + activity);

//                        if (activity == null) {
//                            removeList.add(weakReference);
//                        }
                    }
//                    weakReferenceList.removeAll(removeList);

                }


            }
        }).start();

    }

    @Override
    public void onCreate() {
        super.onCreate();

//        BlockCanary.install(this, new AppBlockCanaryContext()).start();
        BlockCanary.install(this, new BlockCanaryContext()).start();
//        blockDetect();
    }

    private static final LinkedHashMap<Long, String> sStackMap = new LinkedHashMap<>();
    private int mMaxEntryCount = 100;

    private void blockDetect() {
        HandlerThread handlerThread = new HandlerThread("MyBlockCanary");
        handlerThread.start();
        final Handler handler = new Handler(handlerThread.getLooper());


        Looper.getMainLooper().setMessageLogging(new Printer() {

            public long mStartTimeMillis;
            byte[] bytes = new byte[1024 * 1024 * 100];
            StackTraceElement[] stackTrace;

            @Override
            public void println(String x) {

                if (!mStartedPrinting) {
                    mStartTimeMillis = System.currentTimeMillis();

                    mStartedPrinting = true;
                    handler.postDelayed(mRunnable,80);

                    new Thread().start();
                } else {
                    final long endTime = System.currentTimeMillis();
                    mStartedPrinting = false;
                    long time = endTime - mStartTimeMillis;
                    Log.e(TAG, "println: " + time);
                    if (time > 100) {
                        Toast.makeText(getApplicationContext(), "anr了  耗时：" + time, Toast.LENGTH_SHORT).show();
                        for (Map.Entry<Long, String> entry : sStackMap.entrySet()) {
                            Log.e(TAG, "println: "+entry.getKey()+"  " + entry.getValue());
                        }

//                        Map<Thread, StackTraceElement[]> allStackTraces = Thread.getAllStackTraces();
//                        for (StackTraceElement stackTraceElement : allStackTraces.get(Thread.currentThread())) {
//                            Log.e(TAG, "println1: " + stackTraceElement.toString());
//                        }


                    }
                    handler.removeCallbacks(mRunnable);

                }

            }
        });

    }
    private Runnable mRunnable = new Runnable() {
        @Override
        public void run() {

            StringBuilder stringBuilder = new StringBuilder();

            for (StackTraceElement stackTraceElement : Looper.getMainLooper().getThread().getStackTrace()) {
                stringBuilder
                        .append(stackTraceElement.toString())
                        .append(BlockInfo.SEPARATOR);
            }

            synchronized (sStackMap) {
                if (sStackMap.size() == mMaxEntryCount && mMaxEntryCount > 0) {
                    sStackMap.remove(sStackMap.keySet().iterator().next());
                }
                sStackMap.put(System.currentTimeMillis(), stringBuilder.toString());
            }



//            for (StackTraceElement stackTraceElement : Looper.getMainLooper().getThread().getStackTrace()) {
//                Log.e(TAG, "println: " + stackTraceElement.toString());
//            }

        }

    };
    private void hookAMS() {
        try {
            Class<?> AMNClass = Class.forName("android.app.ActivityManagerNative");
            Field gDefaultField = AMNClass.getDeclaredField("gDefault");
            gDefaultField.setAccessible(true);
            Object gDefault = gDefaultField.get(null);
            Method[] declaredMethods = gDefault.getClass().getDeclaredMethods();

            Field instanceField = gDefault.getClass().getDeclaredField("mInstance");
            instanceField.setAccessible(true);
            Object invokeRawManager = instanceField.get(gDefault);

            Log.e(TAG, "hookAMS:instanceField " + invokeRawManager);

            Log.e(TAG, "hookAMS:gDefault " + gDefault);
            Class singleton = Class.forName("android.util.Singleton");

            Field mInstanceField = singleton.getDeclaredField("mInstance");
            mInstanceField.setAccessible(true);
            final Object rawActivityManager = mInstanceField.get(gDefault);
            Log.e(TAG, "hookAMS:rawActivityManager " + rawActivityManager);
            Class<?> iActivityManagerInterface = Class.forName("android.app.IActivityManager");
            Log.e(TAG, "hookAMS:iActivityManagerInterface " + iActivityManagerInterface);
            Object proxyInstance = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{iActivityManagerInterface}, new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    Log.e(TAG, "hookAMS:invoke " + method.getName());
                    if ("startActivity".equals(method.getName())) {
                        int index = 0;
                        for (int i = 0; i < args.length; i++) {
                            if (args[i] instanceof Intent) {
                                Log.e(TAG, "hookAMS:Intent " + args[i]);
                                index = i;
                            }
                        }

                        Intent intent = new Intent();
                        ComponentName componentName = new ComponentName("com.mj.test.hooktest", EmptyActivity.class.getName());
                        intent.setComponent(componentName);


                        args[index] = intent;
                        return method.invoke(rawActivityManager, args);
                    }

                    return method.invoke(rawActivityManager, args);
                }
            });

            mInstanceField.set(gDefault, proxyInstance);

        } catch (Exception e) {
            Log.e(TAG, "hookAMS:Exception " + e.toString());
            e.printStackTrace();
        }
    }
}
